package com.game.core.db.service.jedis;

import java.util.Collections;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.game.core.db.service.jedis.util.JedisByteHashMap;
import com.game.core.db.service.jedis.util.SafeEncoder;

public final class BuilderFactory {
	public static final Builder<Double> DOUBLE = new Builder<Double>() {
		@Override
		public Double build(Object data) {
			String asString = STRING.build(data);
			return asString == null ? null : Double.valueOf(asString);
		}

		@Override
		public String toString() {
			return "double";
		}
	};
	public static final Builder<Boolean> BOOLEAN = new Builder<Boolean>() {
		@Override
		public Boolean build(Object data) {
			return ((Long) data) == 1;
		}

		@Override
		public String toString() {
			return "boolean";
		}
	};
	public static final Builder<byte[]> BYTE_ARRAY = new Builder<byte[]>() {
		@Override
		public byte[] build(Object data) {
			return ((byte[]) data); // deleted == 1
		}

		@Override
		public String toString() {
			return "byte[]";
		}
	};

	public static final Builder<Long> LONG = new Builder<Long>() {
		@Override
		public Long build(Object data) {
			return (Long) data;
		}

		@Override
		public String toString() {
			return "long";
		}

	};
	public static final Builder<String> STRING = new Builder<String>() {
		@Override
		public String build(Object data) {
			return data == null ? null : SafeEncoder.encode((byte[]) data);
		}

		@Override
		public String toString() {
			return "string";
		}

	};
	public static final Builder<List<String>> STRING_LIST = new Builder<List<String>>() {
		@Override
		@SuppressWarnings("unchecked")
		public List<String> build(Object data) {
			if (null == data) {
				return Collections.emptyList();
			}
			List<byte[]> l = (List<byte[]>) data;
			final ArrayList<String> result = new ArrayList<String>(l.size());
			for (final byte[] barray : l) {
				if (barray == null) {
					result.add(null);
				} else {
					result.add(SafeEncoder.encode(barray));
				}
			}
			return result;
		}

		@Override
		public String toString() {
			return "List<String>";
		}

	};
	public static final Builder<Map<String, String>> STRING_MAP = new Builder<Map<String, String>>() {
		@Override
		@SuppressWarnings("unchecked")
		public Map<String, String> build(Object data) {
			final List<byte[]> flatHash = (List<byte[]>) data;
			final Map<String, String> hash = new HashMap<String, String>(flatHash.size() / 2, 1);
			final Iterator<byte[]> iterator = flatHash.iterator();
			while (iterator.hasNext()) {
				hash.put(SafeEncoder.encode(iterator.next()), SafeEncoder.encode(iterator.next()));
			}

			return hash;
		}

		@Override
		public String toString() {
			return "Map<String, String>";
		}

	};

	public static final Builder<Map<String, String>> PUBSUB_NUMSUB_MAP = new Builder<Map<String, String>>() {
		@Override
		@SuppressWarnings("unchecked")
		public Map<String, String> build(Object data) {
			final List<Object> flatHash = (List<Object>) data;
			final Map<String, String> hash = new HashMap<String, String>(flatHash.size() / 2, 1);
			final Iterator<Object> iterator = flatHash.iterator();
			while (iterator.hasNext()) {
				hash.put(SafeEncoder.encode((byte[]) iterator.next()), String.valueOf((Long) iterator.next()));
			}

			return hash;
		}

		@Override
		public String toString() {
			return "PUBSUB_NUMSUB_MAP<String, String>";
		}

	};

	public static final Builder<Set<String>> STRING_SET = new Builder<Set<String>>() {
		@Override
		@SuppressWarnings("unchecked")
		public Set<String> build(Object data) {
			if (null == data) {
				return Collections.emptySet();
			}
			List<byte[]> l = (List<byte[]>) data;
			final Set<String> result = new HashSet<String>(l.size(), 1);
			for (final byte[] barray : l) {
				if (barray == null) {
					result.add(null);
				} else {
					result.add(SafeEncoder.encode(barray));
				}
			}
			return result;
		}

		@Override
		public String toString() {
			return "Set<String>";
		}

	};

	public static final Builder<List<byte[]>> BYTE_ARRAY_LIST = new Builder<List<byte[]>>() {
		@Override
		@SuppressWarnings("unchecked")
		public List<byte[]> build(Object data) {
			if (null == data) {
				return Collections.emptyList();
			}
			List<byte[]> l = (List<byte[]>) data;

			return l;
		}

		@Override
		public String toString() {
			return "List<byte[]>";
		}
	};

	public static final Builder<Set<byte[]>> BYTE_ARRAY_ZSET = new Builder<Set<byte[]>>() {
		@Override
		@SuppressWarnings("unchecked")
		public Set<byte[]> build(Object data) {
			if (null == data) {
				return Collections.emptySet();
			}
			List<byte[]> l = (List<byte[]>) data;
			final Set<byte[]> result = new LinkedHashSet<byte[]>(l);
			for (final byte[] barray : l) {
				if (barray == null) {
					result.add(null);
				} else {
					result.add(barray);
				}
			}
			return result;
		}

		@Override
		public String toString() {
			return "ZSet<byte[]>";
		}
	};
	public static final Builder<Map<byte[], byte[]>> BYTE_ARRAY_MAP = new Builder<Map<byte[], byte[]>>() {
		@Override
		@SuppressWarnings("unchecked")
		public Map<byte[], byte[]> build(Object data) {
			final List<byte[]> flatHash = (List<byte[]>) data;
			final Map<byte[], byte[]> hash = new JedisByteHashMap();
			final Iterator<byte[]> iterator = flatHash.iterator();
			while (iterator.hasNext()) {
				hash.put(iterator.next(), iterator.next());
			}

			return hash;
		}

		@Override
		public String toString() {
			return "Map<byte[], byte[]>";
		}

	};

	public static final Builder<Set<String>> STRING_ZSET = new Builder<Set<String>>() {
		@Override
		@SuppressWarnings("unchecked")
		public Set<String> build(Object data) {
			if (null == data) {
				return Collections.emptySet();
			}
			List<byte[]> l = (List<byte[]>) data;
			final Set<String> result = new LinkedHashSet<String>(l.size(), 1);
			for (final byte[] barray : l) {
				if (barray == null) {
					result.add(null);
				} else {
					result.add(SafeEncoder.encode(barray));
				}
			}
			return result;
		}

		@Override
		public String toString() {
			return "ZSet<String>";
		}

	};

	public static final Builder<Set<Tuple>> TUPLE_ZSET = new Builder<Set<Tuple>>() {
		@Override
		@SuppressWarnings("unchecked")
		public Set<Tuple> build(Object data) {
			if (null == data) {
				return Collections.emptySet();
			}
			List<byte[]> l = (List<byte[]>) data;
			final Set<Tuple> result = new LinkedHashSet<Tuple>(l.size() / 2, 1);
			Iterator<byte[]> iterator = l.iterator();
			while (iterator.hasNext()) {
				result.add(new Tuple(SafeEncoder.encode(iterator.next()),
						Double.valueOf(SafeEncoder.encode(iterator.next()))));
			}
			return result;
		}

		@Override
		public String toString() {
			return "ZSet<Tuple>";
		}

	};

	public static final Builder<Set<Tuple>> TUPLE_ZSET_BINARY = new Builder<Set<Tuple>>() {
		@Override
		@SuppressWarnings("unchecked")
		public Set<Tuple> build(Object data) {
			if (null == data) {
				return Collections.emptySet();
			}
			List<byte[]> l = (List<byte[]>) data;
			final Set<Tuple> result = new LinkedHashSet<Tuple>(l.size() / 2, 1);
			Iterator<byte[]> iterator = l.iterator();
			while (iterator.hasNext()) {
				result.add(new Tuple(iterator.next(), Double.valueOf(SafeEncoder.encode(iterator.next()))));
			}

			return result;

		}

		@Override
		public String toString() {
			return "ZSet<Tuple>";
		}
	};

	public static final Builder<Object> EVAL_RESULT = new Builder<Object>() {

		@Override
		public Object build(Object data) {
			return evalResult(data);
		}

		@Override
		public String toString() {
			return "Eval<Object>";
		}

		private Object evalResult(Object result) {
			if (result instanceof byte[])
				return SafeEncoder.encode((byte[]) result);

			if (result instanceof List<?>) {
				List<?> list = (List<?>) result;
				List<Object> listResult = new ArrayList<Object>(list.size());
				for (Object bin : list) {
					listResult.add(evalResult(bin));
				}

				return listResult;
			}

			return result;
		}

	};

	public static final Builder<Object> EVAL_BINARY_RESULT = new Builder<Object>() {

		@Override
		public Object build(Object data) {
			return evalResult(data);
		}

		@Override
		public String toString() {
			return "Eval<Object>";
		}

		private Object evalResult(Object result) {
			if (result instanceof List<?>) {
				List<?> list = (List<?>) result;
				List<Object> listResult = new ArrayList<Object>(list.size());
				for (Object bin : list) {
					listResult.add(evalResult(bin));
				}

				return listResult;
			}

			return result;
		}

	};

	public static final Builder<List<GeoCoordinate>> GEO_COORDINATE_LIST = new Builder<List<GeoCoordinate>>() {
		@Override
		public List<GeoCoordinate> build(Object data) {
			if (null == data) {
				return Collections.emptyList();
			}
			return interpretGeoposResult((List<Object>) data);
		}

		@Override
		public String toString() {
			return "List<GeoCoordinate>";
		}

		private List<GeoCoordinate> interpretGeoposResult(List<Object> responses) {
			List<GeoCoordinate> responseCoordinate = new ArrayList<GeoCoordinate>(responses.size());
			for (Object response : responses) {
				if (response == null) {
					responseCoordinate.add(null);
				} else {
					List<Object> respList = (List<Object>) response;
					GeoCoordinate coord = new GeoCoordinate(
							Double.parseDouble(SafeEncoder.encode((byte[]) respList.get(0))),
							Double.parseDouble(SafeEncoder.encode((byte[]) respList.get(1))));
					responseCoordinate.add(coord);
				}
			}
			return responseCoordinate;
		}
	};

	public static final Builder<List<GeoRadiusResponse>> GEORADIUS_WITH_PARAMS_RESULT = new Builder<List<GeoRadiusResponse>>() {
		@Override
		public List<GeoRadiusResponse> build(Object data) {
			if (data == null) {
				return Collections.emptyList();
			} else {
				List<Object> objectList = (List<Object>) data;

				if (objectList.isEmpty()) {
					return new ArrayList<GeoRadiusResponse>();
				}

				List<GeoRadiusResponse> responses = new ArrayList<GeoRadiusResponse>(objectList.size());
				if (objectList.get(0) instanceof List<?>) {
					// list of members with additional informations
					GeoRadiusResponse resp;
					for (Object obj : objectList) {
						List<Object> informations = (List<Object>) obj;

						resp = new GeoRadiusResponse((byte[]) informations.get(0));

						int size = informations.size();
						for (int idx = 1; idx < size; idx++) {
							Object info = informations.get(idx);
							if (info instanceof List<?>) {
								// coordinate
								List<Object> coord = (List<Object>) info;

								resp.setCoordinate(new GeoCoordinate(convertByteArrayToDouble(coord.get(0)),
										convertByteArrayToDouble(coord.get(1))));
							} else {
								// distance
								resp.setDistance(convertByteArrayToDouble(info));
							}
						}

						responses.add(resp);
					}
				} else {
					// list of members
					for (Object obj : objectList) {
						responses.add(new GeoRadiusResponse((byte[]) obj));
					}
				}

				return responses;
			}
		}

		private Double convertByteArrayToDouble(Object obj) {
			return Double.valueOf(SafeEncoder.encode((byte[]) obj));
		}

		@Override
		public String toString() {
			return "GeoRadiusWithParamsResult";
		}
	};

	public static final Builder<List<Long>> LONG_LIST = new Builder<List<Long>>() {
		@Override
		@SuppressWarnings("unchecked")
		public List<Long> build(Object data) {
			if (null == data) {
				return null;
			}
			return (List<Long>) data;
		}

		@Override
		public String toString() {
			return "List<Long>";
		}

	};

	private BuilderFactory() {
		throw new InstantiationError("Must not instantiate this class");
	}

}
